﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using élénkPróbája.native;
using élénkPróbája.native.rule;

namespace élénkPróbája
{
    public class LitmusValidatorDescriptor<T> : ILitValidatorDescriptor
    {
        protected IEnumerable<IValidationRule> Rules { get; private set; }

        public LitmusValidatorDescriptor(IEnumerable<IValidationRule> ruleBuilders)
        {
            Rules = ruleBuilders;
        }

        public virtual string GetName(string property)
        {
            var nameUsed = Rules
                .OfType<PropRule>()
                .Where(x => x.PropertyName == property)
                .Select(x => x.GetDisplayName()).FirstOrDefault();

            return nameUsed;
        }

        public virtual ILookup<string, ILitPropertyValidator> GetMembersWithValidators()
        {
            var query = from rule in Rules.OfType<PropRule>()
                        where rule.PropertyName != null
                        from validator in rule.Validators
                        select new { propertyName = rule.PropertyName, validator };

            return query.ToLookup(x => x.propertyName, x => x.validator);
        }

        public IEnumerable<ILitPropertyValidator> GetValidatorsForMember(string name)
        {
            return GetMembersWithValidators()[name];
        }

        public IEnumerable<IValidationRule> GetRulesForMember(string name)
        {
            var query = from rule in Rules.OfType<PropRule>()
                        where rule.PropertyName == name
                        select (IValidationRule)rule;

            return query.ToList();
        }

        public virtual string GetName(Expression<Func<T, object>> propertyExpression)
        {
            var member = propertyExpression.GetMember();

            if (member == null)
            {
                throw new ArgumentException(
                    $"Cannot retrieve name as expression '{propertyExpression}' as it does not specify a property.");
            }

            return GetName(member.Name);
        }

        public IEnumerable<ILitPropertyValidator> GetValidatorsForMember<TValue>(MemberAccessor<T, TValue> accessor)
        {
            return from rule in Rules.OfType<PropRule>()
                   where Equals(rule.Member, accessor.Member)
                   from validator in rule.Validators
                   select validator;
        }


        public IEnumerable<RulesetMetadata> GetRulesByRuleset()
        {
            var query = from rule in Rules.OfType<PropRule>()
                        group rule by rule.RuleSet
                into grp
                        select new RulesetMetadata(grp.Key, grp);

            return query.ToList();
        }

        public class RulesetMetadata
        {
            public RulesetMetadata(string name, IEnumerable<PropRule> rules)
            {
                Name = name;
                Rules = rules;
            }

            public string Name { get; private set; }
            public IEnumerable<PropRule> Rules { get; private set; }
        }
    }
}